/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2021年11月21日
 */
package com.massyframework.beanskin.runtime.service.serialize;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import com.massyframework.beanskin.ModuleNotFoundException;
import com.massyframework.beanskin.SimpleModuleIdentifier;
import com.massyframework.beanskin.service.serialize.Serializer;
import com.massyframework.beanskin.util.IOUtils;
import com.massyframework.beanskin.util.classloading.ClassLoaderUtils;
import com.massyframework.beanskin.util.classloading.ModuleClassLoader;


/**
 * 抽象的序列化器
 * @author huangkh
 *
 */
public abstract class AbstractSerializer implements Serializer {

	/* 内核装配件模块标识符 */
	protected static String IDENTIFIER_KERNEL =
			SimpleModuleIdentifier.createModuleIdentifierWithKernel()
				.toIdentifier();
	/**
	 * 
	 */
	public AbstractSerializer() {
		
	}

	/**
	 * 执行序列化
	 * @param target {@link Object},待序列化的对象
	 * @param additionalModuleIdentifier {@link boolean},是否需要附加模块标识符信息
	 * @return {@link byte[]},数组
	 * @throws IOException
	 */
	public byte[] doSerial(Object target, boolean additionalModuleIdentifier) throws IOException {
		if (target == null) return new byte[0];
		
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(os);
			if (additionalModuleIdentifier) {
				ClassLoader loader = target.getClass().getClassLoader();
				if (loader instanceof ModuleClassLoader) {
					oos.writeUTF(((ModuleClassLoader)loader).toIdentifier());
				}else {
					oos.writeUTF(IDENTIFIER_KERNEL);
				}
			}
			
			oos.writeObject(target);
			return os.toByteArray();
		}finally {
			IOUtils.close(oos);
			IOUtils.close(os);
		}
	}

	/**
	 * 执行反序列化
	 * @param bytes {@link bytes}
	 * @param additionalModuleIdentifier {@link boolean},是否需要附加模块标识符信息
	 * @return {@link Object}
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public Object doDeserial(byte[] bytes, boolean additionalModuleIdentifier) throws IOException, ClassNotFoundException, ModuleNotFoundException {
		ByteArrayInputStream is = new ByteArrayInputStream(bytes);
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(is);
			
			String identifierString = null;
			if (additionalModuleIdentifier) {
				identifierString = ois.readUTF();
			}
			
			ClassLoader loader = this.retrieveClassLoader(identifierString);
			ClassLoader contextLoader = ClassLoaderUtils.setThreadContextClassLoader(loader);
			try {
				return ois.readObject();
			}finally {
				Thread.currentThread().setContextClassLoader(contextLoader);
			}
		}finally {
			if (ois != null) {
				try {
					ois.close();
				}catch(Exception e) {}
			}
			
			try {
				is.close();
			}catch(Exception e) {}
		}
	}

	/**
	 * 取回{@code identifier}对应的类加载器
	 * 
	 * @param identifierString {@link String},可能为null.
	 * @return {@link ClassLoader}
	 */
	protected abstract ClassLoader retrieveClassLoader(String identifierString) throws ModuleNotFoundException;
}
